const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor) {
  this.status = PENDING;

  this.value = null;

  this.reson = null;

  this.successCallback = [];

  this.failCallback = [];

  this.resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
debugger
      for (let func of this.successCallback) {
        try{
          func(this.value);
        }catch(error){
          console.error(error)
        }
      }
    }
  };

  this.reject = (error) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = error;
      debugger
      for (let func of this.failCallback) {
        try{
          func(this.reason);
        }catch(error){
          console.error(error)
        }
      }
    }
  };

  this.resolveMyPromise = (value, p, resolve, reject) => {
    if (value === p) {
      return reject(
        new TypeError(
          "Uncaught (in mypromise) TypeError: Chaining cycle detected for mypromise #<MyPromise>"
        )
      );
    }

    if (value instanceof MyPromise) {
      value.then(resolve, reject);
    } else {
      resolve(value);
    }
  };

  try {
    executor(this.resolve, this.reject);
  } catch (error) {
    console.log(error);
  }
}

MyPromise.prototype.then = function (successCallback, failCallback) {
  successCallback =
    typeof successCallback === "function" ? successCallback : () => {};

  failCallback =
    typeof failCallback === "function"
      ? failCallback
      : (reason) => {
          throw reason;
        };

  const p = new MyPromise((resolve, reject) => {
    if (this.status === FULFILLED) {
      // 成功, 是同步代码
      queueMicrotask(() => {
        try {
          const result = successCallback(this.value);
          this.resolveMyPromise(result, p, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    } else if (this.status === REJECTED) {
      // 失败, 是同步代码
      queueMicrotask(() => {
        try {
          const result = failCallback(this.reason);
          this.resolveMyPromise(result, p, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    } else {
      // pending, 是异步代码
      this.successCallback.push(() => {
        queueMicrotask(() => {
          try {
            const result = successCallback(this.value);
            this.resolveMyPromise(result, p, resolve, reject);
          } catch (error) {
            reject(this.reason);
          }
        });
      });
      this.failCallback.push(() => {
        queueMicrotask(() => {
          try {
            const result = failCallback(this.reason);
            this.resolveMyPromise(result, p, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      });
    }
  });
  return p;
};

MyPromise.prototype.catch = function (failCallback) {
  failCallback =
    typeof failCallback === "function"
      ? failCallback
      : () => {
          throw this.reason;
        };
  return this.then(undefined, failCallback);
};

MyPromise.prototype.finally = function (callback) {
  return this.then(
    (r) => MyPromise.resolve(callback).then((v) => v),
    (e) =>
      MyPromise.resolve(callback).then(() => {
        throw e;
      })
  );
};

MyPromise.resolve = function (value) {
  if (value instanceof MyPromise) {
    return value.then((value) => value);
  } else {
    return new MyPromise(function (resolve) {
      resolve(value);
    });
  }
};

MyPromise.reject = function (value) {
  if (value instanceof MyPromise) {
    return value.then(undefined, this.reject);
  } else {
    return new MyPromise(function (resolve, reject) {
      reject(value);
    });
  }
};

MyPromise.all = function (list) {
  let result = [];
  return new MyPromise(function (resolve, reject) {
    try {
      let index = 0;
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === list.length) {
          resolve(result);
        }
      }
      for (let i = 0, length = list.length; i < length; i++) {
        if (list[i] instanceof MyPromise) {
          list[i].then((r) => addData(i, r), reject);
        } else {
          addData(i, list[i]);
        }
      }
    } catch (error) {
      reject(error);
    }
  });
};

MyPromise.any = function (list) {
  return new MyPromise(function (resolve, reject) {
    try {
      let len = list.filter((item) => item instanceof MyPromise).length;
      let index = 0;
      let firstError = null;
      for (let i = 0, length = list.length; i < length; i++) {
        if (list[i] instanceof MyPromise) {
          list[i].then(resolve).catch((e) => {
            if (index === 0) {
              firstError = e;
            }
            index++;
            if (index === len) {
              reject(firstError);
            }
          });
        } else {
          resolve(list[i]);
        }
      }
    } catch (error) {
      reject(error);
    }
  });
};

MyPromise.race = function (list) {
  return new MyPromise(function (resolve, reject) {
    try {
      for (let i = 0, length = list.length; i < length; i++) {
        if (list[i] instanceof MyPromise) {
          list[i].then(resolve, reject);
        } else {
          resolve(list[i]);
        }
      }
    } catch (error) {
      reject(error);
    }
  });
};

MyPromise.allSettled = function (list) {
  let result = [];
  return new MyPromise(function (resolve, reject) {
    try {
      let index = 0;
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === list.length) {
          resolve(result);
        }
      }
      for (let i = 0, length = list.length; i < length; i++) {
        if (list[i] instanceof MyPromise) {
          list[i].then(
            (value) => {
              addData(i, {
                status: FULFILLED,
                value,
              });
            },
            (reason) => {
              addData(i, {
                status: REJECTED,
                reason,
              });
            }
          );
        } else {
          addData(i, {
            status: FULFILLED,
            value: list[i],
          });
        }
      }
    } catch (error) {
      reject(error);
    }
  });
};